#include "Sound.h"

#include "Game.h"

#include <fmod_errors.h>
#include <iostream>
#include <sstream>

FMOD::System* Sound::system = NULL;
FMOD::Sound* Sound::sound[SAMPLES_COUNT];
FMOD::Channel* Sound::channel = 0;

const std::string Sound::folder = "res/sounds/";
const std::string Sound::extension = ".wav";
std::string Sound::path[SAMPLES_COUNT];

const FMOD_VECTOR Sound::fmodUpVector = {0.0f, -1.0f, 0.0f};
const FMOD_VECTOR Sound::fmodNullVector = {0.0f, 0.0f, 0.0f};

float Sound::cooldown = 0.0f;

void Sound::ErrorCheck(FMOD_RESULT result) {
	if (result != FMOD_OK) {
		Game::log.LogError(StringBuilder() << "FMOD: (" << result << ") " << FMOD_ErrorString(result));
	}
}

void Sound::Init() {
	path[SAMPLE_STONE1] = folder + "stone1" + extension;
	path[SAMPLE_STONE2] = folder + "stone2" + extension;
	path[SAMPLE_STONE3] = folder + "stone3" + extension;
	path[SAMPLE_KNOCK] = folder + "knock" + extension;
	path[SAMPLE_SCROLL] = folder + "scroll" + extension;
	path[SAMPLE_EXPLOSION] = folder + "explosion" + extension;
	path[SAMPLE_SHOOT] = folder + "shoot" + extension;
	path[SAMPLE_CLEAR] = folder + "clear" + extension;

	FMOD_RESULT result;

	result = FMOD::System_Create(&system);
	ErrorCheck(result);

	unsigned int version;
	result = system->getVersion(&version);
	ErrorCheck(result);

	if (version < FMOD_VERSION) {
		Game::log.LogError(StringBuilder() << "FMOD: You are using an old version of FMOD " << version << ". This program requires " << FMOD_VERSION << ".");
	}

	result = system->init(128, FMOD_INIT_NORMAL, 0);
	if (result == FMOD_ERR_OUTPUT_CREATEBUFFER) {
		result = system->setSpeakerMode(FMOD_SPEAKERMODE_STEREO);
		ErrorCheck(result);

		result = system->init(128, FMOD_INIT_NORMAL, 0);
		ErrorCheck(result);
	}

	const float distanceFactor = 5.0f;
	result = system->set3DSettings(1.0, distanceFactor, 1.0f);
	ErrorCheck(result);

	for(int i=0; i<SAMPLES_COUNT; ++i) {
		result = system->createSound(path[i].c_str(), FMOD_3D, 0, &sound[i]);
		ErrorCheck(result);

		result = sound[i]->setVariations(1000.0f, 0.0f, 0.0f);
		ErrorCheck(result); 

		result = sound[i]->setMode(FMOD_LOOP_OFF);
		ErrorCheck(result);
	}
}

void Sound::Play(SAMPLE sample, float volume, const NxVec3& sourcePosition, const NxVec3& listenerForward) {
	const bool ignoringCooldown = true;
	if(ignoringCooldown || cooldown <= 0) {
		FMOD_RESULT result;

		const FMOD_VECTOR srcPos = { sourcePosition.x, sourcePosition.y, sourcePosition.z };

		result = system->playSound(FMOD_CHANNEL_FREE, sound[sample], false, &channel);
		ErrorCheck(result);

		result = channel->setVolume(volume);
		ErrorCheck(result);

		result = channel->setMode(FMOD_3D);
		ErrorCheck(result);

		result = channel->set3DAttributes(&srcPos, &fmodNullVector);
		ErrorCheck(result);

		const FMOD_VECTOR forward = {listenerForward.x, listenerForward.y, listenerForward.z};
		result = system->set3DListenerAttributes(0, &fmodNullVector, &fmodNullVector, &forward, &fmodUpVector);
		ErrorCheck(result);

		result = system->update();
		ErrorCheck(result);

		cooldown = 0.0001f;
	}
}

void Sound::Play(SAMPLE sample, float volume) {
	FMOD_RESULT result;

	result = system->playSound(FMOD_CHANNEL_FREE, sound[sample], false, &channel);
	ErrorCheck(result);

	result = channel->setVolume(volume);
	ErrorCheck(result);

	result = channel->setMode(FMOD_2D);
	ErrorCheck(result);

	result = system->update();
	ErrorCheck(result);
}

void Sound::Tick(float elapsedTime) {
	cooldown -= elapsedTime;
	if(cooldown < 0) {
		cooldown = 0;
	}
}














